package com.braska.grave.netty.bean.aware;

import com.alibaba.fastjson.JSONArray;
import com.braska.grave.netty.codec.JSONDecoder;
import com.braska.grave.netty.codec.JSONEncoder;
import com.braska.grave.netty.handler.NettyClientHandler;
import com.braska.grave.netty.listener.ConnectionListener;
import com.braska.grave.netty.manager.NettyChannelManager;
import com.braska.grave.netty.model.Request;
import com.braska.grave.netty.model.Response;
import com.braska.grave.netty.sender.RequestSender;
import com.google.common.collect.Lists;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.net.SocketAddress;
import java.util.concurrent.SynchronousQueue;
import java.util.logging.Logger;

/**
 * @author shenyuhang
 * @date 2020/3/31
 **/
public class NettyClientApplicationContextAware extends ChannelInitializer<SocketChannel>
        implements RequestSender, ApplicationContextAware, InitializingBean {
    private static final Logger logger = Logger.getLogger(NettyClientApplicationContextAware.class.getName());

    private String remoteAddress;
    private Bootstrap bootstrap;
    private EventLoopGroup group;
    private NettyChannelManager manager;
    private NettyClientHandler handler;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.remoteAddress = applicationContext.getEnvironment().getProperty("remoteAddress");
        this.bootstrap = new Bootstrap();
        this.group = new NioEventLoopGroup(1);
        this.bootstrap.group(group).
                channel(NioSocketChannel.class).
                option(ChannelOption.TCP_NODELAY, true).
                option(ChannelOption.SO_KEEPALIVE, true).
                handler(this);
        this.manager = new NettyChannelManager(this);
        this.handler = new NettyClientHandler(manager, remoteAddress);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.manager.refresh(Lists.newArrayList(remoteAddress));
    }

    @Override
    public Object send(Request request) throws InterruptedException {
        Channel channel = manager.take();
        if (channel != null && channel.isActive()) {
            SynchronousQueue<Object> queue = this.handler.sendRequest(request, channel);
            Object result = queue.take();
            return JSONArray.toJSONString(result);
        } else {
            Response res = new Response();
            res.setCode(1);
            res.setError("未正确连接到服务器.请检查相关配置信息!");
            return JSONArray.toJSONString(res);
        }
    }

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast(new IdleStateHandler(0, 0, 30));
        pipeline.addLast(new JSONEncoder());
        pipeline.addLast(new JSONDecoder());
        pipeline.addLast(this.handler);
    }

    @Override
    public Channel connect(SocketAddress address) throws InterruptedException {
        ChannelFuture future = bootstrap.connect(address);
        future.addListener(new ConnectionListener(this.manager, this.remoteAddress));
        Channel channel = future.channel();//future.sync().channel();
        return channel;
    }

    public void destroy() {
        this.group.shutdownGracefully();
    }
}
